Skip to content

fs storage: remove empty directories after object deletion#55

Draft
Copilot wants to merge 3 commits into
masterfrom
copilot/add-cleanup-empty-folders
Draft

fs storage: remove empty directories after object deletion#55
Copilot wants to merge 3 commits into
masterfrom
copilot/add-cleanup-empty-folders

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 9, 2026

  • Explore codebase and understand filesystem storage implementation
  • Add cleanupEmptyFolders helper to pkg/storages/fs/folder.go
  • Modify DeleteObjects to call cleanupEmptyFolders after each file deletion
  • Add tests in folder_test.go to verify empty directories are removed after deletion
  • Use filepath.Dir/filepath.Clean/filepath.Rel instead of path/strings.HasPrefix for correct OS path handling
  • Handle ENOTEMPTY race condition: treat non-empty removal failure as a stop condition, not an error
  • Run linter and tests to verify changes (all pass)
Original prompt

This section details on the original issue you should resolve

<issue_title>[FEATURE] cleanup folder on filesystem storage</issue_title>
<issue_description>### What feature would you like?

when local filesystem storage is used (for example, configured using WALG_FILE_PREFIX statement), folders are created for new backup, but empty folders are not deleted when backup is deleted (you do not need that for S3 storage)

for example the following command deletes files, but keep empty folders

wal-g delete retain FULL 3 --confirm

we need to delete empty folders as well

also please make sure that "go lint" and "go fmt" checks are passed

Why is this feature useful?

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: chipitsine <2217296+chipitsine@users.noreply.github.com>
Copilot AI changed the title [WIP] Add cleanup for empty folders on filesystem storage fs storage: remove empty directories after object deletion Mar 9, 2026
@chipitsine chipitsine requested a review from Copilot March 9, 2026 21:14
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Removes empty directory trees left behind when deleting objects from local filesystem storage (WALG_FILE_PREFIX), aligning behavior with object-store semantics.

Changes:

  • Add post-delete cleanup that walks up parent directories and removes them if empty (stopping at storage root).
  • Add tests asserting empty directories are pruned and non-empty directories are preserved.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
pkg/storages/fs/folder.go Adds empty-parent-directory cleanup after object deletion.
pkg/storages/fs/folder_test.go Adds tests covering directory cleanup behavior after deletions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread pkg/storages/fs/folder.go Outdated
Comment on lines +65 to +67
if err = folder.cleanupEmptyFolders(path.Dir(filePath)); err != nil {
return err
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path.Dir / path.Clean operate on slash-separated paths (URL paths), not OS filesystem paths. On Windows (and even in some edge cases on Unix when separators differ), this can yield incorrect parent traversal and root comparisons, preventing cleanup or cleaning the wrong directories. Use filepath.Dir and filepath.Clean for filesystem paths, and avoid strings.HasPrefix for path containment checks (it can misclassify /tmp/root2 as within /tmp/root); instead prefer filepath.Rel(rootPath, dirPath) and stop when the rel path is . or starts with .. (or is ..).

Copilot uses AI. Check for mistakes.
Comment thread pkg/storages/fs/folder.go Outdated
Comment on lines +74 to +79
rootPath := path.Clean(folder.rootPath)
for {
dirPath = path.Clean(dirPath)
if dirPath == rootPath || !strings.HasPrefix(dirPath, rootPath) {
break
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path.Dir / path.Clean operate on slash-separated paths (URL paths), not OS filesystem paths. On Windows (and even in some edge cases on Unix when separators differ), this can yield incorrect parent traversal and root comparisons, preventing cleanup or cleaning the wrong directories. Use filepath.Dir and filepath.Clean for filesystem paths, and avoid strings.HasPrefix for path containment checks (it can misclassify /tmp/root2 as within /tmp/root); instead prefer filepath.Rel(rootPath, dirPath) and stop when the rel path is . or starts with .. (or is ..).

Copilot uses AI. Check for mistakes.
Comment thread pkg/storages/fs/folder.go
Comment on lines +87 to +95
if len(entries) > 0 {
break
}
if err = os.Remove(dirPath); err != nil {
if os.IsNotExist(err) {
break
}
return fmt.Errorf("unable to remove empty directory %q: %w", dirPath, err)
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a race between os.ReadDir and os.Remove: a directory can become non-empty after the read, causing os.Remove to fail with ENOTEMPTY/EEXIST. Returning an error here can make DeleteObjects fail even though the object deletion succeeded. Treat “directory not empty” removal failures as a normal stop condition (break cleanup) rather than an error, while still erroring on other failure types.

Copilot uses AI. Check for mistakes.
@chipitsine
Copy link
Copy Markdown
Owner

@copilot apply changes based on the comments in this thread

…eanupEmptyFolders

Co-authored-by: chipitsine <2217296+chipitsine@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Mar 9, 2026

@copilot apply changes based on the comments in this thread

Applied all three review comments in commit 6e08500:

  1. Replaced path.Dir/path.Clean/strings.HasPrefix with filepath.Dir/filepath.Clean/filepath.Rel for correct OS filesystem path handling and safe root-boundary checks.
  2. Added isNotEmpty helper (syscall.ENOTEMPTY) so a directory-not-empty error from os.Remove (race between ReadDir and Remove) is treated as a stop condition rather than an error.

@chipitsine chipitsine force-pushed the master branch 2 times, most recently from 3633c51 to 81395bd Compare May 7, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] cleanup folder on filesystem storage

3 participants